package jeconbond.experiment.market.resourcerelations.generate;

import jeconbond.automata.INode;
import jeconbond.automata.AutomataUtils.IProfCostManagerFactory;
import jeconbond.automata.AutomataUtils.IProfitCostGenManager;
import jeconbond.economic.resources.IResourceType;
import jeconbond.economic.resources.ResourceBasket;
import jeconbond.economic.resources.ResourceUtils;
import jeconbond.experiment.market.resourcerelations.GraigExperimentSettings;
import jeconbond.experiment.market.resourcerelations.process.IGraigExperimentContext;
import jeconbond.experiment.process.IJEBExperimentContext;
import laboratoryQ.BaseUtils;
import laboratoryQ.computation.IRandom;

public class GoalGroupComplementaryBasedProfitCostGen implements IProfitCostGenManager, IProfCostManagerFactory {
	private static final int COST_SIZE = 4;
	public static final IProfCostManagerFactory FACTORY_INSTANCE = new GoalGroupComplementaryBasedProfitCostGen();

	private IGraigExperimentContext experimentContext;
	private IResourceType goalResource;
	private double[] probabSums;
	public int costSize = COST_SIZE;
	/**
	 * Design note: it is object's state. But it was the easiest solution to observe that onBranchCompleted() called.
	 */
	private boolean onBranchCompletedCalled = false;

	public GoalGroupComplementaryBasedProfitCostGen(
			IGraigExperimentContext experimentContext,
			IResourceType goalResource
	) {
		this.experimentContext = experimentContext;
		this.goalResource = goalResource;
		onBranchCompleted();
	}

	/**
	 * 4 factory
	 */
	private GoalGroupComplementaryBasedProfitCostGen() {}

	@Override
	public ResourceBasket[] generateCosts() {
		if (!onBranchCompletedCalled) {
			throw new RuntimeException("Call onBranchCompleted() befor new Branch creation.");
		}
		GraigExperimentSettings settings = experimentContext.getExperimentSettings();
		ResourceBasket[] result = ResourceUtils.randomBaskets(
				settings.resources,
				experimentContext.getRandom(),
				(int) (costSize * (2.0 / 3.0)), 
				costSize
		);
		IRandom random = experimentContext.getRandom();
		for (ResourceBasket rb : result) {
			double normByType = 1.1;
			if (random.get() < normByType) {
				rb.type = chooseCostResource();
			}
			normByVal(rb);
		}
		return result;
	}

	@Override
	public ResourceBasket[] generateProfit() {
		if (!onBranchCompletedCalled) {
			throw new RuntimeException("Call onBranchCompleted() befor new Branch creation.");
		}
		onBranchCompletedCalled = false;
		ResourceBasket profit = new ResourceBasket(
				goalResource,
				experimentContext.getRandom().get()
		);
		return new ResourceBasket[] { profit };
	}

	@Override
	public double addRandomCost(INode[] automatas, IResourceType resource) {
		throw new UnsupportedOperationException();
	}
	
	@Override
	public double addRandomProfit(INode[] automatas, IResourceType resource) {
		throw new UnsupportedOperationException();
	}

	@Override
	public void onBranchCompleted() {
		probabSums = (double[]) BaseUtils.randomElement(
				experimentContext.getRandom(), 
				experimentContext.getComplementaryProbabSums()
		); 
		onBranchCompletedCalled = true;
	}
	
	@Override
	public IProfitCostGenManager newManager(
			IJEBExperimentContext iexperimentContext
	) {
		IGraigExperimentContext experimentContext = 
			(IGraigExperimentContext) iexperimentContext;
		int goalResourcePos = experimentContext.nextGoalPosition();
		return new GoalGroupComplementaryBasedProfitCostGen(
				experimentContext,
				experimentContext.getExperimentSettings().resources[goalResourcePos]
		);
	}

	private IResourceType chooseCostResource() {
		int resPos = BaseUtils.randomDistributed(
				probabSums, 
				experimentContext.getRandom()
				);
		return experimentContext.getExperimentSettings().resources[resPos];
	}

	/**
	 * reduce resource value according 2 probabilities. 
	 */
	private void normByVal(ResourceBasket rb) {
//		int rn = 0;
//		GraigExperimentSettings settings = experimentContext.getExperimentSettings();
//		for (;;rn++) {
//			if (settings.resources[rn] == rb.type) {
//				break;
//			}
//		}
//		double part = probabSums[rn];
//		part -= rn == 0 ? 0 : probabSums[rn - 1];
//		part /= probabSums[probabSums.length - 1];
//		rb.value *= part;
	}

}
